#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>

//用一个cdev支持多个设备
#define CHAR_DEV_MAJOR 256
#define CHAR_DEV_MINOR 0
#define CHAR_DEV_CNT 2
#define CHAR_DEV_NAME "one-driver-more-device-1"

//定义一个cdev
static struct cdev vsdev;

//定义多个fifo
DEFINE_KFIFO(vsfifo0, char, 32);
DEFINE_KFIFO(vsfifo1, char, 32);

static int one_driver_more_dev_1_open(struct inode *inode, struct file *filp)
{

    //指向不同的KFIFO
    switch (MINOR(inode->i_rdev))
    {
    default:
    case 0:
        filp->private_data = &vsfifo0;
        break;
    case 1:
        filp->private_data = &vsfifo1;
        break;
    }
    return 0;
}

static int one_driver_more_dev_1_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t one_driver_more_dev_1_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
    int ret;
    unsigned int copied = 0;
    struct kfifo *vsfifo = filp->private_data;

    ret = kfifo_to_user(vsfifo, buf, count, &copied);

    return ret == 0 ? copied : ret;
}

static ssize_t one_driver_more_dev_1_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
    int ret;
    unsigned int copied = 0;
    struct kfifo *vsfifo = filp->private_data;

    ret = kfifo_from_user(vsfifo, buf, count, &copied);

    return ret == 0 ? copied : ret;
}

// 用来定义操作字符设备的方法
static struct file_operations vsdev_ops = {
    .owner = THIS_MODULE,                     //只想当前模块
    .open = one_driver_more_dev_1_open,       //打开
    .release = one_driver_more_dev_1_release, //释放
    .read = one_driver_more_dev_1_read,       //读数据
    .write = one_driver_more_dev_1_write,     //写数据
};

static int __init one_driver_more_dev_1_driver_init(void)
{
    int ret;
    dev_t dev;

    //合成设备号
    dev = MKDEV(CHAR_DEV_MAJOR, CHAR_DEV_MINOR);
    //注册设备号
    ret = register_chrdev_region(dev, CHAR_DEV_CNT, CHAR_DEV_NAME);
    if (ret)
        goto reg_err;

    //初始化字符设备
    cdev_init(&vsdev, &vsdev_ops);
    vsdev.owner = THIS_MODULE;

    //将主设备号对255取余添加到cdev_map中
    ret = cdev_add(&vsdev, dev, CHAR_DEV_CNT);
    if (ret)
        goto add_err;

    return 0;
add_err:
    unregister_chrdev_region(dev, CHAR_DEV_CNT);
reg_err:
    return ret;
}

static void __exit one_driver_more_dev_1_driver_exit(void)
{
    dev_t dev;

    dev = MKDEV(CHAR_DEV_MAJOR, CHAR_DEV_MINOR);

    //删除cdev
    cdev_del(&vsdev);
    //注销设备号
    unregister_chrdev_region(dev, CHAR_DEV_CNT);
}

module_init(one_driver_more_dev_1_driver_init);
module_exit(one_driver_more_dev_1_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Biven Tang <bivent@163.com>");
MODULE_DESCRIPTION("one driver more device with one cdev more fifo!");
MODULE_ALIAS("one-driver-more-device-1");
